﻿using System;
using System.Collections;
using System.Collections.Generic;

namespace NetAssist.Collections
{
  /// <summary>
  /// Represents a unique first-in, first-out (FIFO) collection of objects.
  /// </summary>
  /// <typeparam name="T">Specifies the type of elements in the queue.</typeparam>
  public class UniqueQueue<T> : IEnumerable<T>, ICollection, IEnumerable
  {
    private List<T> uniqueQueue;

    /// <summary>
    /// Initializes a new instance of the UniqueQueue&lt;T&gt; class that is empty and has the default initial capacity.
    /// </summary>
    public UniqueQueue()
    {
      uniqueQueue = new List<T>();
    }

    /// <summary>
    /// Initializes a new instance of the UniqueQueue&lt;T&gt; class that contains elements copied from the specified collection and has sufficient  capacity to accommodate the number of elements copied.
    /// </summary>
    /// <param name="collection">The collection whose elements are copied to the new UniqueQueue&lt;T&gt;.</param>    
    /// <exception cref="System.ArgumentNullException">collection is null.</exception>
    public UniqueQueue(IEnumerable<T> collection)
    {
      uniqueQueue = new List<T>(collection);
    }

    /// <summary>
    /// Initializes a new instance of the UniqueQueue&lt;T&gt; class that is empty and has the specified initial capacity.
    /// </summary>
    /// <param name="capacity">The initial number of elements that the UniqueQueue&lt;T&gt; can contain.</param>
    /// <exception cref="System.ArgumentOutOfRangeException">capacity is less than zero.</exception>
    public UniqueQueue(int capacity)
    {
      uniqueQueue = new List<T>(capacity);
    }

    /// <summary>
    /// Gets the number of elements contained in the UniqueQueue&lt;T&gt;.
    /// </summary>
    /// <returns>The number of elements contained in the UniqueQueue&lt;T&gt;.</returns>
    public int Count { get { return uniqueQueue.Count; } }

    /// <summary>
    /// Removes all objects from the UniqueQueue&lt;T&gt;.
    /// </summary>
    public void Clear()
    {
      uniqueQueue.Clear();
    }

    /// <summary>
    /// Determines whether an element is in the UniqueQueue&lt;T&gt;.
    /// </summary>
    /// <param name="item">The object to locate in the UniqueQueue&lt;T&gt;. The value can be null for reference types.</param>
    /// <returns>true if item is found in the UniqueQueue&lt;T&gt;; otherwise, false.</returns>
    public bool Contains(T item)
    { return uniqueQueue.Contains(item); }

    /// <summary>
    /// Copies the UniqueQueue&lt;T&gt; elements to an existing one-dimensional System.Array, starting at the specified array index.
    /// </summary>
    /// <param name="array">The one-dimensional System.Array that is the destination of the elements copied from UniqueQueue&lt;T&gt;. The System.Array must have zero-based indexing.</param>
    /// <param name="arrayIndex">The zero-based index in array at which copying begins.</param>
    /// <exception cref="System.ArgumentNullException">array is null.</exception>
    /// <exception cref="System.ArgumentOutOfRangeException">arrayIndex is less than zero.</exception>
    /// <exception cref="System.ArgumentException">The number of elements in the source UniqueQueue&lt;T&gt; is greater than the available space from arrayIndex to the end of the destination array.</exception>
    public void CopyTo(T[] array, int arrayIndex)
    {
      uniqueQueue.CopyTo(array, arrayIndex);
    }

    /// <summary>
    /// Removes and returns the object at the beginning of the UniqueQueue&lt;T&gt;.
    /// </summary>
    /// <returns>The object that is removed from the beginning of the UniqueQueue&lt;T&gt;.</returns>
    /// <exception cref="System.InvalidOperationException">The UniqueQueue&lt;T&gt; is empty.</exception>
    public T Dequeue()
    {      
      T temp = uniqueQueue[0];
      uniqueQueue.RemoveAt(0);
      return temp;
    }

    /// <summary>
    /// Adds an object to the end of the UniqueQueue&lt;T&gt;. if an instance on the object exists elsewhere in the list, it will be removed.
    /// </summary>
    /// <param name="item">The object to add to the UniqueQueue&lt;T&gt;. The value can be null for reference types.</param>
    /// <remarks>If the object already exists in the UniqueQueue&lt;T&gt;, it is removed and than added to the end of the UniqueStack&lt;T&gt;.</remarks>
    public void Enqueue(T item)
    {
      if (uniqueQueue.Contains(item))
        uniqueQueue.Remove(item);

      uniqueQueue.Add(item);
    }

    /// <summary>
    /// Returns the object at the beginning of the UniqueQueue&lt;T&gt; without removing it.
    /// </summary>
    /// <returns>The object at the beginning of the UniqueQueue&lt;T&gt;.</returns>
    /// <exception cref="System.InvalidOperationException">The UniqueQueue&lt;T&gt; is empty.</exception>
    public T Peek()
    {
      return uniqueQueue[0];
    }

    /// <summary>
    /// Copies the UniqueQueue&lt;T&gt; elements to a new array.
    /// </summary>
    /// <returns>A new array containing elements copied from the UniqueQueue&lt;T&gt;.</returns>
    public T[] ToArray()
    {
      return uniqueQueue.ToArray();
    }

    /// <summary>
    /// Sets the capacity to the actual number of elements in the UniqueQueue&lt;T&gt;, if that number is less than 90 percent of current capacity.    
    /// </summary>
    public void TrimExcess()
    {
      uniqueQueue.TrimExcess();
    }

    /// <summary>
    /// Returns an enumerator that iterates through a collection.
    /// </summary>
    /// <returns>An System.Collections.IEnumerator object that can be used to iterate through the collection.</returns>
    public IEnumerator<T> GetEnumerator()
    {
      return uniqueQueue.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
      return this.GetEnumerator();
    }

    /// <summary>
    /// Copies the elements of the System.Collections.ICollection to an System.Array, starting at a particular System.Array index.
    /// </summary>
    /// <param name="array">The one-dimensional System.Array that is the destination of the elements copied from System.Collections.ICollection. The System.Array must have zero-based indexing.</param>
    /// <param name="index">The zero-based index in array at which copying begins.</param>
    /// <exception cref="System.ArgumentNullException">array is null.</exception>
    /// <exception cref="System.ArgumentOutOfRangeException">index is less than zero.</exception>
    /// <exception cref="System.ArgumentException">array is multidimensional -or- The number of elements in the source System.Collections.ICollection is greater than the available space from index to the end of the destination array.</exception>   
    /// <exception cref="System.ArgumentException">The type of the source System.Collections.ICollection cannot be cast automatically to the type of the destination array.</exception>
    public void CopyTo(Array array, int index)
    {
      Helper.InvalidateNullParam(array, "array");
      foreach (T item in this.ToArray())
      {
        array.SetValue(item, index);
        index++;
      }
    }

    /// <summary>
    /// Gets a value indicating whether access to the System.Collections.ICollection is synchronized (thread safe).
    /// </summary>
    /// <returns>true if access to the System.Collections.ICollection is synchronized (thread safe); otherwise, false.</returns>
    public bool IsSynchronized
    {
      get { return false; }
    }

    /// <summary>
    /// Gets an object that can be used to synchronize access to the System.Collections.ICollection.
    /// </summary>
    /// <returns>An object that can be used to synchronize access to the System.Collections.ICollection.</returns>
    public object SyncRoot
    {
      get { return this; }
    }

    /// <summary>
    /// Converts a UniqueQueue&lt;T&gt; to a Queue&lt;T&gt;.
    /// </summary>
    /// <param name="queue">The UniqueQueue&lt;T&gt; to convert.</param>
    /// <returns>The UniqueQueue&lt;T&gt; as a Queue&lt;T&gt;</returns>
    public static implicit operator Queue<T>(UniqueQueue<T> queue)
    {
      Helper.InvalidateNullParam(queue, "queue");
      return new Queue<T>(queue.uniqueQueue);
    }
  }
}
